home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / glibc108.gz / glibc108 / glibc-1.08.1 / stdio / __vfscanf.c < prev    next >
C/C++ Source or Header  |  1994-02-03  |  13KB  |  562 lines

  1. /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
  2. This file is part of the GNU C Library.
  3.  
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public License as
  6. published by the Free Software Foundation; either version 2 of the
  7. License, or (at your option) any later version.
  8.  
  9. The GNU C Library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. Library General Public License for more details.
  13.  
  14. You should have received a copy of the GNU Library General Public
  15. License along with the GNU C Library; see the file COPYING.LIB.  If
  16. not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  17. Cambridge, MA 02139, USA.  */
  18.  
  19. #include <ansidecl.h>
  20. #include <localeinfo.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <ctype.h>
  24. #include <stdarg.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28.  
  29.  
  30. #ifdef    __GNUC__
  31. #define    HAVE_LONGLONG
  32. #define    LONGLONG    long long
  33. #else
  34. #define    LONGLONG    long
  35. #endif
  36.  
  37.  
  38. #define    inchar()    ((c = getc(s)) == EOF ? EOF : (++read_in, c))
  39. #define    conv_error()    return ((c == EOF || ungetc(c, s)), done)
  40. #define input_error()    return (done == 0 ? EOF : done)
  41. #define    memory_error()    return ((errno = ENOMEM), EOF)
  42.  
  43.  
  44. /* Read formatted input from S according to the format string
  45.    FORMAT, using the argument list in ARG.
  46.    Return the number of assignments made, or -1 for an input error.  */
  47. int
  48. DEFUN(__vfscanf, (s, format, arg),
  49.       FILE *s AND CONST char *format AND va_list argptr)
  50. {
  51.   va_list arg = (va_list) argptr;
  52.  
  53.   register CONST char *f = format;
  54.   register char fc;        /* Current character of the format.  */
  55.   register size_t done = 0;    /* Assignments done.  */
  56.   register size_t read_in = 0;    /* Chars read in.  */
  57.   register int c;        /* Last char read.  */
  58.   register int do_assign;    /* Whether to do an assignment.  */
  59.   register int width;        /* Maximum field width.  */
  60.  
  61.   /* Type modifiers.  */
  62.   char is_short, is_long, is_long_double;
  63. #ifdef    HAVE_LONGLONG
  64.   /* We use the `L' modifier for `long long int'.  */
  65. #define    is_longlong    is_long_double
  66. #else
  67. #define    is_longlong    0
  68. #endif
  69.   int malloc_string;        /* Args are char ** to be filled in.  */
  70.   /* Status for reading F-P nums.  */
  71.   char got_dot, got_e;
  72.   /* If a [...] is a [^...].  */
  73.   char not_in;
  74.   /* Base for integral numbers.  */
  75.   int base;
  76.   /* Signedness for integral numbers.  */
  77.   int number_signed;
  78.   /* Integral holding variables.  */
  79.   long int num;
  80.   unsigned long int unum;
  81.   /* Floating-point holding variable.  */
  82.   LONG_DOUBLE fp_num;
  83.   /* Character-buffer pointer.  */
  84.   register char *str, **strptr;
  85.   size_t strsize;
  86.   /* Workspace.  */
  87.   char work[200];
  88.   char *w;            /* Pointer into WORK.  */
  89.   wchar_t decimal;        /* Decimal point character.  */
  90.  
  91.   if (!__validfp(s) || !s->__mode.__read || format == NULL)
  92.     {
  93.       errno = EINVAL;
  94.       return EOF;
  95.     }
  96.  
  97.   /* Figure out the decimal point character.  */
  98.   if (mbtowc(&decimal, _numeric_info->decimal_point,
  99.          strlen(_numeric_info->decimal_point)) <= 0)
  100.     decimal = (wchar_t) *_numeric_info->decimal_point;
  101.  
  102.   c = inchar();
  103.  
  104.   /* Run through the format string.  */
  105.   while (*f != '\0')
  106.     {
  107.       if (!isascii(*f))
  108.     {
  109.       /* Non-ASCII, may be a multibyte.  */
  110.       int len = mblen(f, strlen(f));
  111.       if (len > 0)
  112.         {
  113.           while (len-- > 0)
  114.         if (c == EOF)
  115.           input_error();
  116.         else if (c == *f++)
  117.           (void) inchar();
  118.         else
  119.           conv_error();
  120.           continue;
  121.         }
  122.     }
  123.  
  124.       fc = *f++;
  125.       if (fc != '%')
  126.     {
  127.       /* Characters other than format specs must just match.  */
  128.       if (c == EOF)
  129.         input_error();
  130.       if (isspace(fc))
  131.         {
  132.           /* Whitespace characters match any amount of whitespace.  */
  133.           while (isspace (c))
  134.         inchar ();
  135.           continue;
  136.         }
  137.       else if (c == fc)
  138.         (void) inchar();
  139.       else
  140.         conv_error();
  141.       continue;
  142.     }
  143.  
  144.       /* Check for the assignment-suppressant.  */
  145.       if (*f == '*')
  146.     {
  147.       do_assign = 0;
  148.       ++f;
  149.     }
  150.       else
  151.     do_assign = 1;
  152.         
  153.       /* Find the maximum field width.  */
  154.       width = 0;
  155.       while (isdigit(*f))
  156.     {
  157.       width *= 10;
  158.       width += *f++ - '0';
  159.     }
  160.       if (width == 0)
  161.     width = -1;
  162.  
  163.       /* Check for type modifiers.  */
  164.       is_short = is_long = is_long_double = malloc_string = 0;
  165.       while (*f == 'h' || *f == 'l' || *f == 'L')
  166.     switch (*f++)
  167.       {
  168.       case 'h':
  169.         /* int's are short int's.  */
  170.         is_short = 1;
  171.         break;
  172.       case 'l':
  173.         if (is_long)
  174.           /* A double `l' is equivalent to an `L'.  */
  175.           is_longlong = 1;
  176.         else
  177.           /* int's are long int's.  */
  178.           is_long = 1;
  179.         break;
  180.       case 'L':
  181.         /* double's are long double's, and int's are long long int's.  */
  182.         is_long_double = 1;
  183.         break;
  184.       case 'a':
  185.         /* String conversions (%s, %[) take a `char **'
  186.            arg and fill it in with a malloc'd pointer.  */
  187.         malloc_string = 1;
  188.         break;
  189.       }
  190.  
  191.       /* End of the format string?  */
  192.       if (*f == '\0')
  193.     conv_error();
  194.  
  195.       /* Find the conversion specifier.  */
  196.       w = work;
  197.       fc = *f++;
  198.       if (fc != '[' && fc != 'c' && fc != 'n')
  199.     /* Eat whitespace.  */
  200.     while (isspace(c))
  201.       (void) inchar();
  202.       switch (fc)
  203.     {
  204.     case '%':    /* Must match a literal '%'.  */
  205.       if (c != fc)
  206.         conv_error();
  207.       break;
  208.  
  209.     case 'n':    /* Answer number of assignments done.  */
  210.       if (do_assign)
  211.         *va_arg(arg, int *) = read_in;
  212.       break;
  213.  
  214.     case 'c':    /* Match characters.  */
  215.       if (do_assign)
  216.         {
  217.           str = va_arg (arg, char *);
  218.           if (str == NULL)
  219.         conv_error ();
  220.         }
  221.  
  222.       if (c == EOF)
  223.         input_error();
  224.  
  225.       if (width == -1)
  226.         width = 1;
  227.  
  228.       if (do_assign)
  229.         {
  230.           do
  231.         *str++ = c;
  232.           while (inchar() != EOF && --width > 0);
  233.         }
  234.       else
  235.         while (inchar() != EOF && width > 0)
  236.           --width;
  237.  
  238.       if (do_assign)
  239.         ++done;
  240.  
  241.       break;
  242.  
  243.     case 's':        /* Read a string.  */
  244. #define STRING_ARG                                  \
  245.       if (do_assign)                              \
  246.         {                                      \
  247.           if (malloc_string)                          \
  248.         {                                  \
  249.           /* The string is to be stored in a malloc'd buffer.  */     \
  250.           strptr = va_arg (arg, char **);                  \
  251.           if (strptr == NULL)                          \
  252.             conv_error ();                          \
  253.           /* Allocate an initial buffer.  */                  \
  254.           strsize = 100;                          \
  255.           *strptr = str = malloc (strsize);                  \
  256.         }                                  \
  257.           else                                  \
  258.         str = va_arg (arg, char *);                      \
  259.           if (str == NULL)                              \
  260.         conv_error ();                              \
  261.         }
  262.       STRING_ARG;
  263.  
  264.       if (c == EOF)
  265.         input_error ();
  266.  
  267.       do
  268.         {
  269.           if (isspace (c))
  270.         break;
  271. #define    STRING_ADD_CHAR(c)                              \
  272.           if (do_assign)                              \
  273.         {                                  \
  274.           *str++ = c;                              \
  275.           if (malloc_string && str == *strptr + strsize)          \
  276.             {                                  \
  277.               /* Enlarge the buffer.  */                  \
  278.               str = realloc (*strptr, strsize * 2);              \
  279.               if (str == NULL)                          \
  280.             {                              \
  281.               /* Can't allocate that much.  Last-ditch effort.  */\
  282.               str = realloc (*strptr, strsize + 1);              \
  283.               if (str == NULL)                      \
  284.                 {                              \
  285.                   /* We lose.  Oh well.                  \
  286.                  Terminate the string and stop converting,    \
  287.                  so at least we don't swallow any input.  */  \
  288.                   (*strptr)[strsize] = '\0';              \
  289.                   ++done;                          \
  290.                   conv_error ();                      \
  291.                 }                              \
  292.               else                              \
  293.                 {                              \
  294.                   *strptr = str;                      \
  295.                   str += strsize;                      \
  296.                   ++strsize;                      \
  297.                 }                              \
  298.             }                              \
  299.               else                              \
  300.             {                              \
  301.               *strptr = str;                      \
  302.               str += strsize;                      \
  303.               strsize *= 2;                          \
  304.             }                              \
  305.             }                                  \
  306.         }
  307.           STRING_ADD_CHAR (c);
  308.         } while (inchar () != EOF && (width <= 0 || --width > 0));
  309.  
  310.       if (do_assign)
  311.         {
  312.           *str = '\0';
  313.           ++done;
  314.         }
  315.       break;
  316.  
  317.     case 'x':    /* Hexadecimal integer.  */
  318.     case 'X':    /* Ditto.  */ 
  319.       base = 16;
  320.       number_signed = 0;
  321.       goto number;
  322.  
  323.     case 'o':    /* Octal integer.  */
  324.       base = 8;
  325.       number_signed = 0;
  326.       goto number;
  327.  
  328.     case 'u':    /* Unsigned decimal integer.  */
  329.       base = 10;
  330.       number_signed = 0;
  331.       goto number;
  332.  
  333.     case 'd':    /* Signed decimal integer.  */
  334.       base = 10;
  335.       number_signed = 1;
  336.       goto number;
  337.  
  338.     case 'i':    /* Generic number.  */
  339.       base = 0;
  340.       number_signed = 1;
  341.  
  342.     number:
  343.       if (c == EOF)
  344.         input_error();
  345.  
  346.       /* Check for a sign.  */
  347.       if (c == '-' || c == '+')
  348.         {
  349.           *w++ = c;
  350.           if (width > 0)
  351.         --width;
  352.           (void) inchar();
  353.         }
  354.  
  355.       /* Look for a leading indication of base.  */
  356.       if (c == '0')
  357.         {
  358.           if (width > 0)
  359.         --width;
  360.           *w++ = '0';
  361.  
  362.           (void) inchar();
  363.  
  364.           if (tolower(c) == 'x')
  365.         {
  366.           if (base == 0)
  367.             base = 16;
  368.           if (base == 16)
  369.             {
  370.               if (width > 0)
  371.             --width;
  372.               (void) inchar();
  373.             }
  374.         }
  375.           else if (base == 0)
  376.         base = 8;
  377.         }
  378.  
  379.       if (base == 0)
  380.         base = 10;
  381.  
  382.       /* Read the number into WORK.  */
  383.       do
  384.         {
  385.           if (base == 16 ? !isxdigit(c) :
  386.           (!isdigit(c) || c - '0' >= base))
  387.         break;
  388.           *w++ = c;
  389.           if (width > 0)
  390.         --width;
  391.         } while (inchar() != EOF && width != 0);
  392.  
  393.       if (w == work ||
  394.           (w - work == 1 && (work[0] == '+' || work[0] == '-')))
  395.         /* There was on number.  */
  396.         conv_error();
  397.  
  398.       /* Convert the number.  */
  399.       *w = '\0';
  400.       if (number_signed)
  401.         num = strtol (work, &w, base);
  402.       else
  403.         unum = strtoul (work, &w, base);
  404.       if (w == work)
  405.         conv_error ();
  406.  
  407.       if (do_assign)
  408.         {
  409.           if (! number_signed)
  410.         {
  411.           if (is_longlong)
  412.             *va_arg (arg, unsigned LONGLONG int *) = unum;
  413.           else if (is_long)
  414.             *va_arg (arg, unsigned long int *) = unum;
  415.           else if (is_short)
  416.             *va_arg (arg, unsigned short int *)
  417.               = (unsigned short int) unum;
  418.           else
  419.             *va_arg(arg, unsigned int *) = (unsigned int) unum;
  420.         }
  421.           else
  422.         {
  423.           if (is_longlong)
  424.             *va_arg(arg, LONGLONG int *) = num;
  425.           else if (is_long)
  426.             *va_arg(arg, long int *) = num;
  427.           else if (is_short)
  428.             *va_arg(arg, short int *) = (short int) num;
  429.           else
  430.             *va_arg(arg, int *) = (int) num;
  431.         }
  432.           ++done;
  433.         }
  434.       break;
  435.  
  436.     case 'e':    /* Floating-point numbers.  */
  437.     case 'E':
  438.     case 'f':
  439.     case 'g':
  440.     case 'G':
  441.       if (c == EOF)
  442.         input_error();
  443.  
  444.       /* Check for a sign.  */
  445.       if (c == '-' || c == '+')
  446.         {
  447.           *w++ = c;
  448.           if (inchar() == EOF)
  449.         /* EOF is only an input error before we read any chars.  */
  450.         conv_error();
  451.           if (width > 0)
  452.         --width;
  453.         }
  454.  
  455.       got_dot = got_e = 0;
  456.       do
  457.         {
  458.           if (isdigit(c))
  459.         *w++ = c;
  460.           else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
  461.         *w++ = c;
  462.           else if (!got_e && tolower(c) == 'e')
  463.         {
  464.           *w++ = 'e';
  465.           got_e = got_dot = 1;
  466.         }
  467.           else if (c == decimal && !got_dot)
  468.         {
  469.           *w++ = c;
  470.           got_dot = 1;
  471.         }
  472.           else
  473.         break;
  474.           if (width > 0)
  475.         --width;
  476.         } while (inchar() != EOF && width != 0);
  477.  
  478.       if (w == work)
  479.         conv_error();
  480.       if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
  481.         conv_error();
  482.  
  483. #ifndef MIB_HACKS
  484.       /* Convert the number.  */
  485.       *w = '\0';
  486.       fp_num = strtod(work, &w);
  487.       if (w == work)
  488.         conv_error();
  489.  
  490.       if (do_assign)
  491.         {
  492.           if (is_long_double)
  493.         *va_arg(arg, LONG_DOUBLE *) = fp_num;
  494.           else if (is_long)
  495.         *va_arg(arg, double *) = (double) fp_num;
  496.           else
  497.         *va_arg(arg, float *) = (float) fp_num;
  498.           ++done;
  499.         }
  500.       break;
  501. #endif /* MIB_HACKS */
  502.  
  503.     case '[':    /* Character class.  */
  504.       STRING_ARG;
  505.  
  506.       if (c == EOF)
  507.         input_error();
  508.  
  509.       if (*f == '^')
  510.         {
  511.           ++f;
  512.           not_in = 1;
  513.         }
  514.       else
  515.         not_in = 0;
  516.  
  517.       while ((fc = *f++) != '\0' && fc != ']')
  518.         {
  519.           if (fc == '-' && *f != '\0' && *f != ']' &&
  520.           w > work && w[-1] <= *f)
  521.         /* Add all characters from the one before the '-'
  522.            up to (but not including) the next format char.  */
  523.         for (fc = w[-1] + 1; fc < *f; ++fc)
  524.           *w++ = fc;
  525.           else
  526.         /* Add the character to the list.  */
  527.         *w++ = fc;
  528.         }
  529.       if (fc == '\0')
  530.         conv_error();
  531.  
  532.       *w = '\0';
  533.       unum = read_in;
  534.       do
  535.         {
  536.           if ((strchr (work, c) == NULL) != not_in)
  537.         break;
  538.           STRING_ADD_CHAR (c);
  539.           if (width > 0)
  540.         --width;
  541.         } while (inchar () != EOF && width != 0);
  542.       if (read_in == unum)
  543.         conv_error ();
  544.  
  545.       if (do_assign)
  546.         {
  547.           *str = '\0';
  548.           ++done;
  549.         }
  550.       break;
  551.  
  552.     case 'p':    /* Generic pointer.  */
  553.       base = 16;
  554.       /* A PTR must be the same size as a `long int'.  */
  555.       is_long = 1;
  556.       goto number;
  557.     }
  558.     }
  559.  
  560.   conv_error();
  561. }
  562.